import {
  Component,
  Input,
  NgModule,
  ElementRef,
  OnDestroy,
  AfterViewInit,
  ApplicationRef,
  ComponentFactoryResolver,
  Injector,
  ComponentRef,
  OnInit,
} from '@angular/core';
import { ComponentPortal, DomPortalOutlet } from '@angular/cdk/portal';
import debounce from 'lodash-es/debounce';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';

import { EXAMPLE_COMPONENTS, LiveExample } from '../../../__auto__/examples-module';
import { CopierService } from '../../copier/copier.service';

import { fuiMessageService, FuiModule } from 'flash-ui';
import { HttpClient } from '@angular/common/http';
import { SampleCode, SRC_KINDS, SrcKind } from 'app/shared/model';
import {
  EDITOR_THEME,
  EDITOR_OPTIONS,
  FORMAT_MAPPING,
} from 'app/shared/editor';

@Component({
  selector: 'fui-example-viewer',
  templateUrl: './example-viewer.component.html',
  styleUrls: ['./example-viewer.component.sass'],
})
export class ExampleViewerComponent implements OnInit, AfterViewInit, OnDestroy {
  /** Component portal for the currently displayed example. */
  selectedPortal: ComponentPortal<any>;

  /** Component ref for the currently displayed example. */
  selectedComponentRef: ComponentRef<any>;

  /** String key of the currently displayed example. */
  _example: string;

  exampleData: LiveExample;

  /** Whether the source for the example is being displayed. */
  showSource = false;

  srcKinds = SRC_KINDS;

  currentSrcKind: SrcKind = SRC_KINDS[0];

  codeEditorElement: HTMLElement;

  codeEditor: monaco.editor.ICodeEditor;

  /** For live edit component **/
  code: SampleCode = new SampleCode();

  editing = false;

  module: NgModule = {
    imports: [
      FuiModule,
    ],
    exports: [],
  };

  example: string;

  constructor(
    private elem: ElementRef,
    private appRef: ApplicationRef,
    private componentFactoryResolver: ComponentFactoryResolver,
    private injector: Injector,
    private copier: CopierService,
    private message: fuiMessageService,
    private http: HttpClient,
  ) {
  }

  ngOnInit() {
    if (this.example && EXAMPLE_COMPONENTS[this.example]) {
      this.exampleData = EXAMPLE_COMPONENTS[this.example];
    }

    if (this.exampleData) {
      this.selectedPortal = new ComponentPortal(this.exampleData.component);
    }
  }

  ngAfterViewInit() {
    if (this.exampleData) {
      if (!this.exampleData.iframe) {
        const exampleElement = this.elem.nativeElement.querySelector(`.fui-example-portal`);
        const portalHost = new DomPortalOutlet(exampleElement, this.componentFactoryResolver, this.appRef, this.injector);
        this.selectedComponentRef = portalHost.attach(this.selectedPortal);
      }
      this.fetchCodes();
    } else {
      console.log('MISSING EXAMPLE: ', this.example);
    }
  }

  ngOnDestroy() {
    this.disposeEditor();
  }

  toggleSourceView(): void {
    this.showSource = !this.showSource;

    setTimeout(() => {
      if (this.showSource) {
        this.initEditor();
      } else {
        this.disposeEditor();
      }
    });
  }

  exampleFileUrl(extension: string) {
    return `/assets/examples/${this.example}.component-${extension.toLowerCase()}.txt`;
  }

  copySource() {
    const model = this.codeEditor.getModel();
    const source = model.getValue();
    if (this.copier.copyText(source)) {
      this.message.success('Code copied');
    } else {
      this.message.error('Copy failed. Please try again!');
    }
  }

  fetchCodes() {
    SRC_KINDS.forEach(srcKind => {
      this.http.get(this.exampleFileUrl(srcKind), { responseType: 'text' })
      .subscribe((response) => {
        this.code.update(srcKind, response);
      });
    });
  }

  updateEditingCodes(srcKind: SrcKind, value: string) {
    this.editing = true;

    const code = Object.assign(new SampleCode(), this.code);
    code.update(srcKind, value);

    this.code = code;
  }

  initEditor() {
    monaco.editor.defineTheme('fuiEditorTheme', EDITOR_THEME);

    monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
      noSemanticValidation: true,
      noSuggestionDiagnostics: true,
    });

    this.codeEditorElement = this.elem.nativeElement.querySelector('.viewer-source-editor');
    this.codeEditor = monaco.editor.create(this.codeEditorElement, {
      language: FORMAT_MAPPING[this.currentSrcKind],
      readOnly: this.exampleData?.iframe,
      theme: 'fuiEditorTheme',
      ...EDITOR_OPTIONS,
    });

    this.codeEditor.setValue(this.code[this.currentSrcKind]);

    this.codeEditor.onDidChangeModelContent(debounce((event) => {
      const currentValue = this.codeEditor.getValue();
      const codeValue = this.code[this.currentSrcKind];
      if (codeValue !== currentValue) {
        this.updateEditingCodes(this.currentSrcKind, currentValue);
      }
    }, 500));
  }

  disposeEditor() {
    if (this.codeEditor) {
      this.codeEditor.dispose();
    }
  }

  getViewId(data: LiveExample) {
    return encodeURIComponent(data.title);
  }

  onSrcKindChange() {
    this.codeEditor.setValue(this.code[this.currentSrcKind]);
    this.codeEditor.revealLine(1);
    const model = this.codeEditor.getModel();
    monaco.editor.setModelLanguage(model, FORMAT_MAPPING[this.currentSrcKind]);
  }
}
